package com.meme.editor.gui.sprite2d;

import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.event.AncestorEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.AncestorListener;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.SystemColor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelListener;
import java.awt.event.ComponentListener;

import org.apache.log4j.Logger;

import com.meme.app.AppRegistry;
import com.meme.editor.event.EntityChangedEvent;
import com.meme.editor.event.EntityObjectListener;
import com.meme.editor.gui.EntityChildrenListView;
import com.meme.editor.gui.PropertyEditPanel;
import com.meme.editor.model.sprite2d.CompositedModule;
import com.meme.editor.model.sprite2d.ModulePart;
import com.meme.editor.model.sprite2d.Module;
import com.meme.editor.EntityObject;

public class CompositedModuleView extends JPanel
	implements EntityObjectListener, ListSelectionListener, AncestorListener, 
	MouseListener, MouseMotionListener, MouseWheelListener//, ComponentListener
{
	static Logger logger = Logger.getLogger (CompositedModuleView.class);
	
	
	static final int MaxScale = 8;
	static final int MinScale = 1;
	static final int DefaultScale = 2;
	
	int mScale = DefaultScale;
	
	int mOriginX;
	int mOriginY;
	
	int mPanelWidth = 0;
	int mPanelHeight = 0;

	CompositedModule mCompositedModule;
	ModulePart   mSelectedModulePart;
	
	EntityChildrenListView mChildrenListView;
//	PropertyEditPanel      mChildPropertyView;
	
	public CompositedModuleView ()
	{		
		addAncestorListener (this);
		addMouseListener    (this);
		addMouseMotionListener (this);
		addMouseWheelListener (this);
	}
	
	
	public void setModule (CompositedModule module)
	{
		if ( mCompositedModule != null )
			mCompositedModule.removeEntityObjectListener (this);
		
		mCompositedModule = module;
//		if ( mChildrenListView != null )
//			mChildrenListView.setEntity (mCompositedModule);
		
		if ( mCompositedModule != null )
			mCompositedModule.addEntityObjectListener (this);
		
//		int width  = getWidth ();
//		int height = getHeight ();
//		mOriginX = width / 2;
//		mOriginY = height / 2;
		
		setSelectedModuleRef (null); 
		
		repaint ();
	}
	
	public void setSelectedModuleRef (ModulePart part)
	{
		mSelectedModulePart = part;
		
//		if ( mChildPropertyView != null )
//			mChildPropertyView.setDataSource (part);
		
		AppRegistry.setRegistry("editor.sprite2d.CompositedModuleView.selectedModulePart", mSelectedModulePart);
		SharedData.sSelectedModuleRef = mSelectedModulePart;
		
		
		if ( part != null )
		{
			if ( mChildrenListView != null )
				mChildrenListView.setSelectedIndex(part.getIndex());

			
			logger.assertLog( mCompositedModule != null, "mCompositedModule should not be null." );
			logger.assertLog( mCompositedModule == mSelectedModulePart.getParent(), "error somewhere." );
		}
		
		repaint ();
	}
	
	public void setChildrenListView (EntityChildrenListView childrenView)
	{
		mChildrenListView = childrenView;
	}
	
//	public void setChildPropertyView (PropertyEditPanel propertyView)
//	{
//		mChildPropertyView = propertyView;
//	}
	
	//void reset

	@Override
	public void repaint ()
	{
		super.repaint();
	}
	
	@Override
	public void paintComponent (Graphics _g)
	{
		Graphics2D g = (Graphics2D)_g;
		int width  = getWidth ();
		int height = getHeight ();
		
		if ( mPanelWidth == 0 || mPanelHeight == 0 )
		{
			mOriginX = width  / 2;
			mOriginY = height / 2;
		}
		else if (mPanelWidth != width || mPanelHeight != height )
		{
			mOriginX = width  * mOriginX / mPanelWidth;
			mOriginY = height * mOriginY / mPanelHeight;
		}
		mPanelWidth  = width;
		mPanelHeight = height;
		
		g.setColor(SystemColor.window);
		g.fillRect(0, 0, width, height);
		g.setColor(Color.red);
		g.drawLine(0, mOriginY, width, mOriginY);
		g.drawLine(mOriginX, 0, mOriginX, height);
		
		if ( mCompositedModule != null )
		{
			mCompositedModule.render(g, mOriginX, mOriginY, mScale, mSelectedModulePart);		
		}
		
		if ( mSelectedModulePart != null )
		{
		}
	}
	
	protected Point lp2dp (Point lp)
	{
		Point dp = new Point ();
		
		dp.x = mOriginX + lp.x * mScale;
		dp.y = mOriginY + lp.y * mScale;
		
		return dp;
	}
	
	protected void _2dp (Point lp)
	{
		Point dp = lp2dp (lp);
		lp.setLocation (dp);
	}
	
	protected Point dp2lp (Point dp)
	{
		Point lp = new Point ();
		
		lp.x = ( dp.x - mOriginX  ) / mScale;
		lp.y = ( dp.y - mOriginY  ) / mScale;
		
		return lp;
	}
	
	protected void _2lp (Point dp)
	{
		Point lp = dp2lp (dp);
		dp.setLocation (lp);
	}
	
	protected Dimension ds2ls (Dimension size)
	{
		return new Dimension (size.width / mScale, size.height / mScale);
	}
	
	protected void _2ls (Dimension size)
	{
		Dimension ls = ds2ls (size);
		size.setSize(ls);
	}
	
	// PropertyManagerListener
	@Override 
	public void notifyEntityChanged (EntityChangedEvent event)
	{ 
		repaint ();
	}
	
	@Override 
	public void valueChanged(ListSelectionEvent e) 
	{
		JList list = (JList) (e.getSource());
		EntityObject entity = (EntityObject) (list.getSelectedValue());

		if ( entity instanceof CompositedModule )
		{
			setModule ((CompositedModule) entity);			
		}
		
		if ( entity instanceof ModulePart )
		{
			setSelectedModuleRef ((ModulePart) entity);
		}
	}
	
	// AncestorListener
	@Override
	public void ancestorAdded(AncestorEvent event) 
	{
		mCompositedModule = null;
		mSelectedModulePart = null;	
		
		SharedData.sSelectedModuleRef = null;
	}

	@Override
	public void ancestorRemoved(AncestorEvent event)  
	{
		if ( mCompositedModule != null )
			mCompositedModule.removeEntityObjectListener (this);
		
		mCompositedModule = null;
		mSelectedModulePart = null;
		
		SharedData.sSelectedModuleRef = null;
	}
	
	@Override
	public void ancestorMoved(AncestorEvent event) 
	{
		
	}
	
	
//--------------------------------------------------------------------------
// as a MouseListener
//--------------------------------------------------------------------------

	boolean mHoldingRightButton;
	
	Point mRightButtonStartPoint = new Point ();
	
	void moveOrigion (Point p)
	{
		  mOriginX += (p.x - mRightButtonStartPoint.x);
		  mOriginY += (p.y - mRightButtonStartPoint.y);
		  
		  if ( mOriginX < 1 )
			  mOriginX = 1;
		  if ( mOriginX >= mPanelWidth - 1)
			  mOriginX = mPanelWidth - 2;
		  if ( mOriginY < 1 )
			  mOriginY = 1;
		  if ( mOriginY >= mPanelHeight - 1)
			  mOriginY = mPanelHeight - 2;
		  
		  mRightButtonStartPoint.setLocation (p);
		  
		  
		  repaint ();
	}
	
	boolean mHoldingLeftButton;
	//int mLastSelectedPartIndex = 0;
	int mLastSelectedPartOffsetX;
	int mLastSelectedPartOffsetY;
	Point mLeftButtonStartPoint = new Point ();
	
	void moveSelectedPart (Point p)
	{
		if ( mSelectedModulePart == null )
			return;
			
		int dx = p.x - mLeftButtonStartPoint.x;
		int dy = p.y - mLeftButtonStartPoint.y;
		Dimension dSize = new Dimension (dx, dy);
		_2ls (dSize);
		
		mSelectedModulePart.setOffsetX (mLastSelectedPartOffsetX + dSize.width);
		mSelectedModulePart.setOffsetY (mLastSelectedPartOffsetY + dSize.height);
		
		mSelectedModulePart.commitModifications();
		  
		repaint ();
	}
	
	void changeSelectedModule (Point dp)
	{
		Point lp = dp2lp (dp);
		
		if ( mCompositedModule != null )
		{
			mSelectedModulePart = mCompositedModule.getChildAtPos(lp.x, lp.y, mSelectedModulePart == null ? 0 : mSelectedModulePart.getIndex());
			setSelectedModuleRef (mSelectedModulePart);
			if ( mSelectedModulePart != null )
			{
				//mLastSelectedPartIndex = mSelectedModulePart.getIndex();
				mLastSelectedPartOffsetX = mSelectedModulePart.getOffsetX();
				mLastSelectedPartOffsetY = mSelectedModulePart.getOffsetY();
				mLeftButtonStartPoint.setLocation(dp);
			}
		}
		
		repaint ();
	}

	//MouseListener
	@Override 
	public void mouseClicked(java.awt.event.MouseEvent e)
	{
	}
	  
	@Override 
	public void mousePressed(java.awt.event.MouseEvent e)
	{
		if (e.getButton () == e.BUTTON1)
		{
			changeSelectedModule (e.getPoint());
			
			mHoldingLeftButton = true;
			
//			 if ( mHoldingLeftButton )
//			 {
//				 updateTempModule (e.getPoint());
//				 
//				 endIempModule ();
//			 }
//			 else
//			 {
//				 startTempModule (e.getPoint());
//			 }
		}
			  
		if (e.getButton () == e.BUTTON3)
		{
			mHoldingRightButton = true;
				  
			mRightButtonStartPoint.setLocation (e.getPoint());
		}
	}
		  
	@Override 
	public void mouseReleased(java.awt.event.MouseEvent e)
	{
//		changeSelectedModule (e.getPoint());
//		AppRegistry.setRegistry("editor.sprite2d.ModuleGroupIconsView.selectedModule", mSelectedModule);

		if (e.getButton () == e.BUTTON1)
		{
			mHoldingLeftButton = false;
		}
		
		if (e.getButton () == e.BUTTON3)
		{
			mHoldingRightButton = false;
			
			//if ()
//			
//			JPopupMenu popMenu = new JPopupMenu();
//			popMenu.add("Replaced by This One");
//			popMenu.show(this, e.getX(), e.getY());
		}
	}
		  
	@Override 
	public void mouseEntered(java.awt.event.MouseEvent arg0)
	{
		
	}
		  
	@Override 
	public void mouseExited(java.awt.event.MouseEvent arg0)
	{
		
	}
	
//--------------------------------------------------------------------------
// as a MouseMotionListener
//--------------------------------------------------------------------------

	  // Method descriptor #9 (Ljava/awt/event/MouseEvent;)V
	  public void mouseDragged(java.awt.event.MouseEvent e)
	  {
		  if ( mHoldingLeftButton )
		  {
			  // move selected Part
			  moveSelectedPart (e.getPoint());
		  }
		  
		  if ( mHoldingRightButton )
		  {
			  // move origin
			  moveOrigion (e.getPoint());
		  }

	  }
	  
	  // Method descriptor #9 (Ljava/awt/event/MouseEvent;)V
	  public void mouseMoved(java.awt.event.MouseEvent e)
	  {

	  }
	  
//--------------------------------------------------------------------------
// as a MouseWheelListener
//--------------------------------------------------------------------------

	  // Method descriptor #8 (Ljava/awt/event/MouseWheelEvent;)V
	  public void mouseWheelMoved(java.awt.event.MouseWheelEvent event)
	  {
		  float scale = (float)mScale + ( event.getWheelRotation () > 0 ? 1 : -1 );
		  
		  if ( scale < MinScale)
			  scale = MinScale;
		  if (scale > MaxScale)
			  scale = MaxScale;
		  
		  mScale = (int) scale;

		  repaint ();
	  }
	  
	  

}
